#pragma once



#define MAX_BRANCHES_PER_RULE 5

#define PARAMS_PER_BRANCH_DESC 1
#define PARAMS_PER_BRANCH_ROT 2

#define TOT_PARAMS_PER_BRANCH 3 // description and rotation params

typedef double ParamVector[TOT_PARAMS_PER_BRANCH];

class LsysBranchRule
{
public:
	struct BranchRotStruct
	{
		double m_lfTheta;
		double m_lfPhi;

		double m_lfPhiDistroWidth;
		double m_lfThetaDistroWidth;

		const BranchRotStruct & operator=(const BranchRotStruct & lVal)
		{
			m_lfTheta = lVal.m_lfTheta; m_lfPhi = lVal.m_lfPhi;
			m_lfPhiDistroWidth = lVal.m_lfPhiDistroWidth;
			m_lfThetaDistroWidth = lVal.m_lfThetaDistroWidth;
			return *this;
		}

		BranchRotStruct(const BranchRotStruct & src)
		{
			*this = src;
		}

		void Crossover(BranchRotStruct branch1, BranchRotStruct branch2, double lfProb1);
		void Creep(double lfCreepWidth);

		void SetRandomly();
		void PrettyRandomize();

		BranchRotStruct()
		{
			PrettyRandomize();
		}
	};

	struct BranchDescr
	{
		BranchRotStruct m_rotParams;
		double m_lfProbability; // normalized, goes between 0 and 1
		double m_lfBranchScale; // normalized, goes between 0 and 1
		double m_lfDistAlongToBranch; // normalized, goes between 0 and 1

		const BranchDescr & operator=(const BranchDescr & lVal)
		{
			m_rotParams = lVal.m_rotParams; m_lfProbability = lVal.m_lfProbability;
			m_lfBranchScale = lVal.m_lfBranchScale; m_lfDistAlongToBranch = lVal.m_lfDistAlongToBranch;
			return *this;
		}

		

		BranchDescr(const BranchDescr & src)
		{
			*this = src;
		}

		void Crossover(BranchDescr branch1, BranchDescr branch2, double lfProb1);
		void Creep(double lfCreepWidth);

		void SetRandomly();
		void PrettyRandomize();

		BranchDescr()
		{
			PrettyRandomize();
		}

	};
private:

	BranchDescr m_BranchDescr[MAX_BRANCHES_PER_RULE];

	double m_lfBaseWidth; // ratio -- length is 1
	double m_lfWidthRatio; // top to bottom

	double m_lfScale; // initially 1

	int m_nSlices, m_nStacks; // for drawing

	void DrawCone();

	void DoTransform(int nWhichChild, int nArrPos, ParamVector * arrParams);

	void InternalRecursiveDraw(int nLevelsLeft, int nChildIdx, int nArrPos, ParamVector * arrParams);
public:
	LsysBranchRule(void);
	~LsysBranchRule(void);

	void Creep(double lfCreepWidth);

	void Crossover(LsysBranchRule branch1, LsysBranchRule branch2, double lfProb1);

	void SetRandomly();

	void PrettyRandomize();

	void Draw(int nLevels, double arrLfProbabilities[][TOT_PARAMS_PER_BRANCH]);

	int GetProbArrNumEnts(int nLevels) const;

	void RandomlySetParamArr(int nLevels, double arrLfProbabilities[][TOT_PARAMS_PER_BRANCH]) const;

	const LsysBranchRule & operator=(const LsysBranchRule & src)
	{
		for(int i =0; i < MAX_BRANCHES_PER_RULE; ++i)
		{
			m_BranchDescr[i]	= src.m_BranchDescr[i];
		}
		m_lfBaseWidth	= src.m_lfBaseWidth;
		m_lfScale		= src.m_lfScale;
		m_lfWidthRatio	= src.m_lfWidthRatio;
		m_nSlices		= src.m_nSlices;
		m_nStacks		= src.m_nStacks;
		return *this;
	}

	LsysBranchRule(const LsysBranchRule & src)
	{
		*this = src;
	}

	
};
